package server.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import plug.result.RetResponse;
import plug.result.RetResult;
import server.config.exception.BizException;
import server.dao.AccountDao;
import server.dao.MoneyRecordDao;
import server.dao.TenantDao;
import server.model.entity.Account;
import server.model.entity.MoneyRecord;
import server.model.entity.Tenant;
import server.model.vo.TenantVo;
import server.service.MoneyRecordService;
import server.service.TenantService;
import server.tool.ClientAgent;

import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: 许哲
 * @createTime: 2021/11/21 17:48
 * @version: 1.0
 */
@Service
@Log4j2
public class TenantServiceImpl implements TenantService {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private TenantDao tenantDao;
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private MoneyRecordDao moneyRecordDao;
    @Autowired
    private MoneyRecordService moneyRecordService;
    @Autowired
    private HttpServletRequest request;

    @Override
    @Transactional
    public RetResult createTenant(TenantVo tenantVo) throws UnsupportedEncodingException {
        MoneyRecord yu = moneyRecordDao.findByIndentOrder(tenantVo.getIndentOrder());
        if (moneyRecordDao.findByIndentOrder(tenantVo.getIndentOrder()) != null) {
            return RetResponse.makeOKRsp("/view/pay?apId=" + yu.getDyOrder());
        }
        sign(tenantVo);
        List<Account> account = accountDao.findByStatus(true).stream().filter(item -> item.getIngMoney().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
        Random df = new Random();
        int number = df.nextInt(account.size());
        Account that = account.get(number);
        MoneyRecord moneyRecord = new MoneyRecord();
        moneyRecord.setDyAccount(that.getAccount());
        moneyRecord.setIndentOrder(tenantVo.getIndentOrder());
        moneyRecord.setMoney(tenantVo.getMoney());
        moneyRecord.setStatus(MoneyRecord.STATUS.ING);
        moneyRecord.setSystemCode(tenantVo.getSystemCode());
        try {
            moneyRecord = moneyRecordDao.save(moneyRecord);
        } catch (Exception e) {
            throw new BizException("订单号重复");
        }
        String url = createWechat(moneyRecord, that);

        Map<String, String> mp = new HashMap<>();
        mp.put("scene", "pc");
        JSONObject risk = new JSONObject();
        int rom = new Random().nextInt(new ClientAgent().getUA().size());
        risk.put("user_agent", new ClientAgent().getUA().get(rom));
        mp.put("risk_info", JSON.toJSONString(risk));
        JSONObject params = new JSONObject();
        moneyRecord = moneyRecordDao.findById(moneyRecord.getId()).get();
        JSONObject si = JSON.parseObject(moneyRecord.getParams(), Feature.OrderedField);
        si.put("return_url", "https://www.douyin.com/pay/union_pay/");
        params.put("params", si);
        mp.put("biz_content", JSON.toJSONString(params));
        JSONObject cache = JSON.parseObject(MoneyRecordServiceImpl.http("https://tp-pay.snssdk.com/gateway-cashier/tp.cashier.trade_create", mp, that.getCookie()));
        if (cache.get("error") == null || cache.getString("error").equalsIgnoreCase("null")) {
            JSONObject process = cache.getJSONObject("process");
            moneyRecord.setCache(JSON.toJSONString(process));
        } else {
            throw new BizException("失败");
        }
        moneyRecord = moneyRecordDao.save(moneyRecord);
        moneyRecordService.instanceMoneyRecord(moneyRecord);
        return RetResponse.makeOKRsp(url);
    }

    private Boolean sign(TenantVo tenantVo) {
        Tenant tenant = tenantDao.findByCode(tenantVo.getSystemCode());
        if (!tenant.getUuid().equalsIgnoreCase(request.getHeader("token"))) {
            throw new BizException("error token");
        }
        if (!tenant.getStatus()) {
            throw new BizException("not exits role");
        }
        if (tenant == null) {
            throw new BizException("not exits key");
        }
        String sign = tenant.getCode() + "+" + tenant.getSecretKey() + "+" + tenantVo.getIndentOrder() + "+" + tenantVo.getMoney();
        if (!getMD5Str(sign).equalsIgnoreCase(tenantVo.getSign())) {
            throw new BizException("not exits key");
        }
        return true;
    }

    private String getMD5Str(String str) {
        byte[] digest = null;
        try {
            MessageDigest md5 = MessageDigest.getInstance("md5");
            digest = md5.digest(str.getBytes("utf-8"));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        //16是表示转换为16进制数
        String md5Str = new BigInteger(1, digest).toString(16);
        return md5Str;
    }

    private String createWechat(MoneyRecord moneyRecord, Account account) throws UnsupportedEncodingException {
        get(account.getAccount(), account.getCookie());
        JSONObject parUrl = post(account.getAccount(), moneyRecord.getMoney(), account.getCookie());
        log.error(JSON.toJSONString(parUrl));
        if (parUrl.getIntValue("status_code") != 0) {
            throw new BizException("支付失败");
        }
        Map<String, String> params = urlSplit(parUrl.getJSONObject("data").getString("params"));
        params.put("sign_type", params.get("sign_type").toUpperCase());
        params.put("trade_no", params.get("trade_no").toUpperCase());
        params.put("trade_type", params.get("trade_type").toUpperCase());
        moneyRecord.setParams(JSON.toJSONString(params));
        String reg = "exts=(.*?)\\&";
        Pattern pattern = Pattern.compile(reg);
        Matcher matcher = pattern.matcher(parUrl.getJSONObject("data").getString("params"));
        String exts = null;
        while (matcher.find()) {
            exts = matcher.group(1);
        }
        exts = exts.replace("{", "%7B");
        exts = exts.replaceAll("\\\\", "");
        exts = exts.replace(":", "%3A");
        exts = exts.replace("}", "%7D");
        params.put("exts", exts);
        String reg2 = "return_url=(.*?)\\&";
        Pattern pattern2 = Pattern.compile(reg2);
        Matcher matcher2 = pattern2.matcher(parUrl.getJSONObject("data").getString("params"));
        String returnUrl = null;
        while (matcher2.find()) {
            returnUrl = matcher2.group(1);
        }
        params.put("sign_type", params.get("sign_type").toUpperCase());
        params.put("trade_no", params.get("trade_no").toUpperCase());
        params.put("trade_type", params.get("trade_type").toUpperCase());
        returnUrl = returnUrl.replace("==", "%3D%3D");
        params.put("return_url", returnUrl);
        moneyRecord.setDyOrder(params.get("trade_no"));
        moneyRecord.setUrl(packageUrl(params).replaceAll("\\\\", ""));
        moneyRecord.setUrl(java.net.URLEncoder.encode(moneyRecord.getUrl(), "UTF-8"));
        moneyRecordDao.save(moneyRecord);
        return "/view/pay?apId=" + moneyRecord.getDyOrder();
    }

    public static JSONObject post(String account, BigDecimal money, String cookie) {
        String path = "https://www.douyin.com/webcast/wallet_api/diamond_buy_external_safe/" +
                "?diamond_id=666666" +
                "&source=8" +
                "&way=0" +
                "&aid=1128" +
                "&short_id=" + account +
                "&customized_price=" + money.intValue() * 100;
        URL url = null;
        HttpURLConnection con = null;
        try {
            url = new URL(path);
            con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("POST");
            int rom = new Random().nextInt(new ClientAgent().getUA().size());
            con.setRequestProperty("User-Agent", new ClientAgent().getUA().get(rom));
            con.setRequestProperty("Cookie", cookie);
            con.connect();
            if (con.getResponseCode() == 200) {
                BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                String line;
                StringBuilder sb = new StringBuilder();
                while ((line = br.readLine()) != null) {// 循环读取流
                    sb.append(line);
                }
                br.close();// 关闭流
                return JSON.parseObject(sb.toString());
            } else {
                throw new BizException("支付失败!");
            }
        } catch (Exception e) {
            throw new BizException("支付失败!");
        } finally {
            con.disconnect();
        }
    }

    public static JSONObject get(String account, String cookie) {
        String path = "https://www.douyin.com/webcast/wallet_api/user_check/?short_id=" + account + "&source=1a0deeb4c56147d0f844d473b325a28b";
        URL url = null;
        HttpURLConnection con = null;
        try {
            url = new URL(path);
            con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");
            int rom = new Random().nextInt(new ClientAgent().getUA().size());
            con.setRequestProperty("User-Agent", new ClientAgent().getUA().get(rom));
            con.setRequestProperty("Cookie", cookie);
            con.connect();
            if (con.getResponseCode() == 200) {
                BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                String line;
                StringBuilder sb = new StringBuilder();
                while ((line = br.readLine()) != null) {// 循环读取流
                    sb.append(line);
                }
                br.close();// 关闭流
                return JSON.parseObject(sb.toString());
            } else {
                throw new BizException("支付失败!");
            }
        } catch (Exception e) {
            throw new BizException("支付失败!");
        } finally {
            con.disconnect();
        }
    }

    private static String truncateUrlPage(String url) {
        String strAllParam = null;
        String[] arrSplit = null;
        url = url.trim().toLowerCase();
        arrSplit = url.split("[?]");
        if (url.length() > 1) {
            if (arrSplit.length > 1) {
                for (int i = 1; i < arrSplit.length; i++) {
                    strAllParam = arrSplit[i];
                }
            }
        }
        return strAllParam;
    }

    public static Map<String, String> urlSplit(String url) {
        Map<String, String> mapRequest = new LinkedHashMap<>();
        String[] arrSplit = null;
        String strUrlParam = truncateUrlPage(url);
        if (strUrlParam == null) {
            return mapRequest;
        }
        arrSplit = strUrlParam.split("[&]");
        for (String strSplit : arrSplit) {
            String[] arrSplitEqual = null;
            arrSplitEqual = strSplit.split("[=]");
            //解析出键值
            if (arrSplitEqual.length > 1) {
                //正确解析
                mapRequest.put(arrSplitEqual[0], arrSplitEqual[1]);
            } else {
                if (arrSplitEqual[0] != "") {
                    //只有参数没有值，不加入
                    mapRequest.put(arrSplitEqual[0], "");
                }
            }
        }
        return mapRequest;
    }

    private String packageUrl(Map<String, String> params) {
        String url = "https://tp-pay.snssdk.com/cashdesk_pc/";
        int i = 0;
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (i == 0) {
                url = url + "?" + entry.getKey() + "=" + entry.getValue();
            } else {
                url = url + "&" + entry.getKey() + "=" + entry.getValue();
            }
            i++;
        }
        return url;
    }
}
